#pragma once
#include <iostream>
#include <pthread.h>
#include <vector>
#include <string>
#include <queue>
#include <unistd.h>
#include <ctime>
using namespace std;

static const int defaultnum = 5;

struct ThreadInfo
{
  pthread_t tid;
  string name;
};

template <class T>
class ThreadPool
{
public:
  T Pop()
  {
    T t = tasks_.front();
    tasks_.pop();
    return t;
  }

  static void *HandlerTask(void *args)
  {
    ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
    string name = tp->GetThreadName(pthread_self());
    while (1)
    {
      tp->Lock();
      while (tp->IsQueueEmpty())
      {
        tp->ThreadSleep();
      }
      T t = tp->Pop();
      tp->UnLock();
      t();
      // 处理任务
      cout << name << " run, " << "result: " << t.GetResult() << endl;
    }
  }

  // 放任务
  void Push(const T &task)
  {
    Lock();
    tasks_.push(task);
    Wakeup();
    UnLock();
  }

  void Start()
  {
    int num = threads_.size();
    for (int i = 0; i < num; i++)
    {
      threads_[i].name = "thread-" + to_string(i + 1);
      pthread_create(&(threads_[i].tid), nullptr, HandlerTask, this);
    }
  }

  static ThreadPool<T> *GetInstance()
  {
    // 单例模式的双重判断，防止每次线程都效率极低地串行获取单例
    if (tp_ == nullptr)
    {
      pthread_mutex_lock(&lock_);
      if (tp_ == nullptr)
      {
        tp_ = new ThreadPool<T>;
      }
      pthread_mutex_unlock(&lock_);
    }
    return tp_;
  }

private:
  ThreadPool(int num = defaultnum)
      : threads_(num)
  {
    pthread_mutex_init(&mutex_, nullptr);
    pthread_cond_init(&cond_, nullptr);
  }

  ThreadPool(const ThreadPool<T> &TP) = delete;

  const ThreadPool<T> &operator=(const ThreadPool<T> &TP) = delete;

  ~ThreadPool()
  {
    pthread_mutex_destroy(&mutex_);
    pthread_cond_destroy(&cond_);
  }

  void Lock()
  {
    pthread_mutex_lock(&mutex_);
  }

  void UnLock()
  {
    pthread_mutex_unlock(&mutex_);
  }

  void Wakeup()
  {
    pthread_cond_signal(&cond_);
  }

  void ThreadSleep()
  {
    pthread_cond_wait(&cond_, &mutex_);
  }

  bool IsQueueEmpty()
  {
    return tasks_.empty();
  }

  string GetThreadName(pthread_t tid)
  {
    int i = 0;
    for (; i < threads_.size(); i++)
    {
      if (tid == threads_[i].tid)
        break;
    }
    return threads_[i].name;
  }

  vector<ThreadInfo> threads_; // 用vector来充当池子
  queue<T> tasks_;             // 任务队列
  pthread_mutex_t mutex_;
  pthread_cond_t cond_;

  static ThreadPool<T> *tp_; // 单例
  static pthread_mutex_t lock_;
};
template <class T>
ThreadPool<T> *ThreadPool<T>::tp_ = nullptr;

template <class T>
pthread_mutex_t ThreadPool<T>::lock_ = PTHREAD_MUTEX_INITIALIZER;